/** \file    segm/Segment.h
  * \brief   Introduction of Segment notion - portion of pixels with some associated data.
  * \date    2001 - 2003
  * \author  Fedor Chelnokov
  * \version $Header: /home/cscom1r4/cvsroot/cs/include/segm/Segment.h,v 1.2 2005/04/04 04:10:16 cscom1r4 Exp $
  */

#ifndef _Segment_h_
#define _Segment_h_

#include <map>
#include <vector>
#include <string>
#include "typelib/types.h"
#include "imglib/color.h"
#include "imglib/imageclass_decl.h"
#include "xtd/Arr2D.h"

typedef std::map<int, int> IntIntMap;

inline small_byte_color int2color(int c)
	{ return reinterpret_cast<small_byte_color&>(c); }

inline int color2int(byte_color c)
	{ c.a = 0;
	  return reinterpret_cast<const int&>(c); }

struct Segment {
//required by xtd::indexed_vector
	typedef int index_type; ///< rgb-color
	index_type index() const
		{ return color2int(color); }

	Segment() {}
	Segment(small_byte_color _color, int _pixelCount=0) : 
		color(_color), pixelCount(_pixelCount) {}
	Segment(int _color, int _pixelCount=0) : 
		color(int2color(_color)), pixelCount(_pixelCount) {}

//obligatory
	small_byte_color			color;			///< color associated with entire segment
	int							pixelCount;		///< total number of pixels in a segment

	bool						active;			///< sometimes means that segment is in mask
	bool						foreign;		///< foreign segment - special case in mask segmentation, the corresponding border segment may be found by its color which is common for both border and foreign segments
	IntIntMap					neighbours;		///< map of neighbour segments, first argument - neighbour's colors, second argument - neighbour's index
	YATL::Arr2d					profile;		///< how the segment being displaced correlates with target frame
	YATL::Arr2i					usedPixels;		///< how many pixels were used to build profile at the same displacement

//profile getter
	double profileAt(int_point i) const
		{ return profile(i.x, i.y); }

//displacements
	void profileResize(int halfSize, double fill);
	void profileResize(int halfSize);			///< farster, do not fill, do not resize usedPixels
	int_point adisplacement() const;			///< absolute displacement of segment
	int_point r2a(int_point rdisplacement) const; ///< converts absolute into relative displacement
	int_point a2r(int_point adisplacement) const; ///< converts relative into absolute displacement
	int_point ppos2a(int_point ppos) const;		///< converts profile position into absolute displacement
	int_point a2ppos(int_point adisplacement ) const;///< converts absolute displacement into profile position
	int_point ppos2r(int_point ppos) const;		///< converts profile position into relative displacement
	int_point r2ppos(int_point rdisplacement) const;///< converts relative displacement into profile position
	const int_point &pdisplacement() const;
	const int_point &rdisplacement() const;
	const int_point &profileCenter() const;
	void setAdisplacement(int_point newVal);	///< with limitation to profile size
	void setRdisplacement(int_point newVal);	///< with limitation to profile size
	void setPdisplacement(int_point newVal);
	void setPpos(int_point newVal);				///< sets relative displacement by position in profile
	void setPposLmt(int_point newVal);			///< with limitation to profile size
	double profileVal() const;					///< profile value at current displacement

//colorStatistics
	small_float_color			meanColor;		///< average color
	double						colorDispersion;///< color dispersion (or up threshold on dispersion)
//position
	mutable int_rect			boundingBox;	///< minimal rectangle that surrounding the segment
	mutable double_point		centre;			///< geometrical centre of the segment
//debug info
	mutable std::string			text;

private:
	int_point				_profileCenter;	///< point in profile associated with absence of relative displacement 
	int_point				_rdisplacement;	///< relative displacement of the segment on target frame relative predicted displacement
	int_point				_pdisplacement;	///< predicted displacement of the segment on target frame = absolute displacement of segment's centre
};

/////////////
// inlines //
/////////////

inline void Segment::profileResize( int halfSize, double fill ) {
	size_t dblsize = 2*halfSize+1; 
	profile.Resize(dblsize, dblsize, fill);
	usedPixels.Resize(dblsize, dblsize, 0);
	_profileCenter = int_point(halfSize,halfSize); //undisturbed
}

inline void Segment::profileResize(int halfSize) {
	size_t dblsize = 2*halfSize+1; 
	profile.Resize(dblsize, dblsize, 0, false);
	_profileCenter = int_point(halfSize,halfSize); //undisturbed
}

inline int_point Segment::adisplacement() const
	{ return _rdisplacement+_pdisplacement; }

inline int_point Segment::r2a( int_point rdisplacement ) const
	{ return rdisplacement+_pdisplacement; }

inline int_point Segment::a2r( int_point adisplacement ) const
	{ return _pdisplacement-adisplacement; }

inline int_point Segment::ppos2a( int_point ppos ) const
	{ return _pdisplacement+ppos-_profileCenter; }

inline int_point Segment::a2ppos( int_point adisplacement ) const
	{ return adisplacement-_pdisplacement+_profileCenter; }

inline int_point Segment::ppos2r( int_point ppos ) const
	{ return ppos-_profileCenter; }

inline int_point Segment::r2ppos( int_point rdisplacement ) const
	{ return rdisplacement+_profileCenter; }

inline const int_point &Segment::pdisplacement() const
	{ return _pdisplacement; }

inline const int_point &Segment::rdisplacement() const
	{ return _rdisplacement; }

inline const int_point &Segment::profileCenter() const
	{ return _profileCenter; }

inline void Segment::setAdisplacement( int_point newVal )
	{ setPposLmt(a2ppos(newVal)); }

inline void Segment::setRdisplacement( int_point newVal )
	{ setPposLmt(r2ppos(newVal)); }

inline void Segment::setPdisplacement( int_point newVal )
	{ _pdisplacement=newVal; }

inline void Segment::setPposLmt( int_point newVal )
	{ newVal.x = limit( newVal.x, 0, profile.Width()-1 );
	  newVal.y = limit( newVal.y, 0, profile.Height()-1 );
	  setPpos( newVal ); }

inline void Segment::setPpos( int_point newVal )
	{ _rdisplacement=newVal-_profileCenter; }

inline double Segment::profileVal() const
	{ return profileAt(_profileCenter + _rdisplacement); }

#endif //_Segment_h_
